using System;
using UnityEngine;
using UnityEngine.Playables;

namespace Taco.Timeline
{
    [ScriptGuid("bd021c94b1615a64db462ca7874e19b6"), IconGuid("af6781f2578301947823eb6557b9a7cc"), Ordered(2), Color(127, 214, 253)]
    public class ParticleTrack : Track<TimelineParticleClipPlayable>
    {
        public override void Bind()
        {
            TrackPlayable = Playable.Create(Timeline.PlayableGraph, Clips.Count);
            base.Bind();
        }

#if UNITY_EDITOR
        public override Type ClipType => typeof(ParticleClip);

        public override Clip AddClip(UnityEngine.Object referenceObject, int frame)
        {
            ParticleClip clip = new ParticleClip((referenceObject as GameObject).GetComponent<ParticleSystem>(), this, frame);
            m_Clips.Add(clip);
            return clip;
        }
        public override bool DragValid()
        {
            return UnityEditor.DragAndDrop.objectReferences.Length == 1 && UnityEditor.DragAndDrop.objectReferences[0] is GameObject gameObject && UnityEditor.EditorUtility.IsPersistent(gameObject) && gameObject.GetComponent<ParticleSystem>();
        }
#endif
    }

    [ScriptGuid("bd021c94b1615a64db462ca7874e19b6"), Color(127, 214, 253)]
    public class ParticleClip : Clip
    {
        [ShowInInspector, OnValueChanged("OnClipChanged", "RebindTimeline")]
        public ParticleSystem ParticleSystem;
        [ShowInInspector, OnValueChanged("RebindTimeline")]
        public string SocketName;
        [ShowInInspector, HideIf("UseSelfTransform"), HorizontalGroup("Position")]
        public Vector3 PositionOffset;
        [ShowInInspector, HideIf("UseSelfTransform"), HorizontalGroup("Rotation")]
        public Vector3 RotationOffset;
        [ShowInInspector(1), OnValueChanged("RebindTimeline", "RepaintInspector")]
        public bool UseSelfTransform = true;

        public TimelineParticleClipPlayable ParticleClipPlayable => ClipPlayable as TimelineParticleClipPlayable;
        public override int Length => ParticleSystem ? Mathf.RoundToInt(ParticleSystem.main.duration * Timeline.FrameRate) : base.Length;
        public ParticleClip(Track track, int frame) : base(track, frame)
        {
        }
        public ParticleClip(ParticleSystem particleSystem, Track track, int frame) : base(track, frame)
        {
            ParticleSystem = particleSystem;
        }

#if UNITY_EDITOR

        public override string Name => ParticleSystem ? ParticleSystem.name : base.Name;
        public override bool ShowExtraPolationMode => true;
        public override ClipCapabilities Capabilities => ClipCapabilities.Resizable | ClipCapabilities.ClipInable;

        [Button("Record"), HideIf("UseSelfTransform"), ShowIf("ShowRecord"), HorizontalGroup("Position")]
        void RecordPosition()
        {
            if (ParticleClipPlayable != null)
            {
                UnityEditor.Undo.RegisterCompleteObjectUndo(Track.Timeline, $"Timeline: RecordPosition");
                PositionOffset = ParticleClipPlayable.ParticleSystem.gameObject.transform.localPosition;
                UnityEditor.EditorUtility.SetDirty(Track.Timeline);                
            }
        }
        [Button("Record"), HideIf("UseSelfTransform"), ShowIf("ShowRecord"), HorizontalGroup("Rotation")]
        void RecordRotation()
        {
            if (ParticleClipPlayable != null)
            {
                UnityEditor.Undo.RegisterCompleteObjectUndo(Track.Timeline, $"Timeline: RecordRotation");
                RotationOffset = ParticleClipPlayable.ParticleSystem.gameObject.transform.localEulerAngles;
                UnityEditor.EditorUtility.SetDirty(Track.Timeline);
            }
        }

        bool ShowRecord()
        {
            return ParticleClipPlayable != null;
        }

        void OnClipChanged()
        {
            OnNameChanged?.Invoke();
        }
#endif
    }

    public class TimelineParticleClipPlayable : TimelineClipPlayable
    {
        public ParticleClip ParticleClip { get; private set; }
        public ParticleSystem ParticleSystem { get; private set; }

        public TimelineParticleClipPlayable() { }

        public override void Initialize(Clip clip, Playable owner, Timeline timeline)
        {
            base.Initialize(clip, owner, timeline);

            ParticleClip = clip as ParticleClip;
            timeline.OnRebind += Destroy;
        }

        public override void SetTime(float time)
        {
            base.SetTime(time);
            ParticleSystem?.Simulate(time);
        }
        public override void OnEnable()
        {
            base.OnEnable();
            if(!ParticleSystem)
                Instantiate();
        }
        public override void OnDisable()
        {
            base.OnDisable();
            double current = Handle.GetTime();
            if (current == 0 || ExtraPolationMode != ExtraPolationMode.Hold)
                Destroy();
        }
        public override void OnDestroy()
        {
            base.OnDestroy();
            Destroy();
        }

        void Instantiate()
        {
            if (ParticleClip.ParticleSystem)
            {
                Transform socketTransform = Timeline.TimelinePlayer.transform;
                var childTransforms = Timeline.TimelinePlayer.GetComponentsInChildren<Transform>();
                foreach (var childTransform in childTransforms)
                {
                    if (childTransform.name == ParticleClip.SocketName)
                    {
                        socketTransform = childTransform;
                        break;
                    }
                }

                ParticleSystem = UnityEngine.Object.Instantiate(ParticleClip.ParticleSystem, socketTransform, false);
                if (!ParticleClip.UseSelfTransform)
                {
                    ParticleSystem.transform.localPosition = ParticleClip.PositionOffset;
                    ParticleSystem.transform.localEulerAngles = ParticleClip.RotationOffset;
                }
            }
        }
        void Destroy()
        {
            if (ParticleSystem)
            {
                UnityEngine.Object.DestroyImmediate(ParticleSystem.gameObject);
                ParticleSystem = null;
            }
        }
    }
}